package server;

import client.PaintAction;
import com.thoughtworks.xstream.XStream;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.*;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.http.*;
import org.jboss.netty.util.CharsetUtil;

import javax.imageio.ImageIO;
import java.io.*;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Created by IntelliJ IDEA.
 * User: Hp
 * Date: 23.04.11
 * Time: 22:26
 * To change this template use File | Settings | File Templates.
 */
public class ServerTCP extends SimpleChannelHandler implements ServerInterface, ChannelPipelineFactory {
    Logger logger = Logger.getLogger("global");  //ServerTCP.class.getCanonicalName()
    ServerModel model;
    private Channel serverChannel;
    private ChannelGroup clients;
    private XStream xstream;


    public ServerTCP() {
        xstream = new XStream();
        xstream.alias("paintaction", PaintAction.class);
    }

    public void setServerModel(ServerModel model) {
        this.model = model;
    }

    public ServerModel getServerModel() {
        return model;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        logger.log(Level.INFO,"Client connected");
        clients.add(e.getChannel());
    }

    @Override
    public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        logger.log(Level.INFO,"Client disconnected");
        clients.remove(e.getChannel());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
        clients.remove(e.getChannel());
        e.getChannel().close();

    }

    ByteArrayOutputStream bufStream = new ByteArrayOutputStream(1 << 20);
    ChannelFuture future;

    public void sendImage(Channel channel) {
        logger.log(Level.INFO,"Image request");
        HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
        response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "image/png");
        synchronized (model) {
            bufStream.reset();
            try {
                ImageIO.write(model.getImage(), "png", bufStream);
            } catch (IOException e) {
                logger.log(Level.WARNING, "Strange IOException", e);
            }
            response.setContent(ChannelBuffers.copiedBuffer(bufStream.toByteArray()));
        }
        response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, response.getContent().readableBytes());
        response.setHeader(ServerConstants.VERSION_HEADER, model.getCount());
        future = channel.write(response);
    }
    public void sendJar(Channel channel){
        logger.log(Level.INFO,"Applet requested");
        HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
        response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "application/java-archive");
        bufStream.reset();
        File f;
        f=new File("CourseWork.jar");

        FileInputStream fis = null;
        try {
            fis = new FileInputStream(f);
        } catch (FileNotFoundException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        int len=0;
        try {
            len=fis.available();
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        byte[] buf=new byte[len];

        try {
            fis.read(buf);
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        response.setContent(ChannelBuffers.copiedBuffer(buf));
        response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, response.getContent().readableBytes());
        response.setHeader(ServerConstants.VERSION_HEADER, model.getCount());
        future = channel.write(response);

    }
    public void sendHTML(Channel channel){
          HttpResponse response=new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
        response.setHeader(HttpHeaders.Names.CONTENT_TYPE,"text/html");
        String htmlResponse="<html>\n<head>\n" +
                "</head>\n"+
                "<body> <applet code=\"client.ClientApplet.class\" width=\"400\" height=\"300\" "
                +"codebase=\".\" archive=\"CourseWork.jar\" > \n"
                +"<param name=\"port\" value=\""+ServerConstants.port+"\"> </param>\n"
                +"</applet>\n</body>\n</html>\n";
        response.setContent(ChannelBuffers.copiedBuffer(htmlResponse,CharsetUtil.UTF_8));
        response.setHeader(HttpHeaders.Names.CONTENT_LENGTH,response.getContent().readableBytes());
        future=channel.write(response);

    }
    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
        HttpRequest request = (HttpRequest) (e.getMessage());

        QueryStringDecoder decoder = new QueryStringDecoder(request.getUri());
        if (decoder.getPath().equals("/image") || decoder.getPath().equals("image")) {
            System.out.println(e.getMessage());
           sendImage(e.getChannel());
        } else if (decoder.getPath().equals("/newaction") || decoder.getPath().equals("newaction")) {
            System.out.println(e.getMessage());
            PaintAction action = (PaintAction) xstream.fromXML(request.getContent().toString(CharsetUtil.UTF_8));
            synchronized (model) {
                model.onAction(action);
            }
        } else if (decoder.getPath().equals("/updates") || decoder.getPath().equals("updates")) {
           // System.out.println(e.getMessage());
            QueryStringDecoder stringDecoder = new QueryStringDecoder(request.getUri());
            int number = Integer.parseInt(stringDecoder.getParameters().get("number").get(0));
            List<PaintAction> actions = null;
            synchronized (model) {
                actions = model.getActions(number);
            }
            if (actions == null) {
                sendImage(e.getChannel());
            } else {
                HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
                response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/xml");
                response.setContent(ChannelBuffers.copiedBuffer(xstream.toXML(actions) + "\r\n", CharsetUtil.UTF_8));
                response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, response.getContent().readableBytes());
                future = e.getChannel().write(response);
            }
        } else if(decoder.getPath().equals("/") || decoder.getPath().equals(" ")){
                   sendHTML(e.getChannel());
        }
         else if(decoder.getPath().equals("/CourseWork.jar") || decoder.getPath().equals("CourseWork.jar")){
                   sendJar(e.getChannel());
        }

        else {
            HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND);
            response.setHeader(HttpHeaders.Names.CONTENT_LENGTH,0);
            future = e.getChannel().write(response);
        }
        if (!HttpHeaders.isKeepAlive(request))
            future.addListener(ChannelFutureListener.CLOSE);
    }

    public void start() {
        ServerBootstrap bootStrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));
        bootStrap.setPipelineFactory(this);
        serverChannel = bootStrap.bind(new InetSocketAddress(ServerConstants.port));
        clients = new DefaultChannelGroup("Clients");
    }
    public void stop(){
        clients.disconnect();
        clients.close();
        serverChannel.close();
    }

    public void applyAction(PaintAction action) {
    }


    public ChannelPipeline getPipeline() throws Exception {
        ChannelPipeline pipe = Channels.pipeline(new HttpRequestDecoder(), new HttpResponseEncoder(), this);
        return pipe;
    }
}
